package com.am.server.api.system.user.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.codec.Base64;
import cn.hutool.crypto.SecureUtil;
import cn.hutool.crypto.symmetric.DES;
import cn.hutool.crypto.symmetric.SymmetricAlgorithm;
import com.am.server.api.system.permission.service.UserPermissionCacheService;
import com.am.server.api.system.role.dao.rdb.RoleDao;
import com.am.server.api.system.role.model.entity.RoleEntity;
import com.am.server.api.system.user.dao.rdb.AdminUserDao;
import com.am.server.api.system.user.model.dto.*;
import com.am.server.api.system.user.model.entity.AdminUserEntity;
import com.am.server.api.system.user.model.mapper.AdminUserMapper;
import com.am.server.api.system.user.service.AdminUserService;
import com.am.server.common.annotation.transaction.Commit;
import com.am.server.common.annotation.transaction.ReadOnly;
import com.am.server.common.base.model.dto.PageDto;
import com.am.server.common.constant.Constant;
import com.am.server.common.constant.WebSocketUriConstant;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.domain.Page;
import org.springframework.messaging.simp.SimpMessagingTemplate;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Optional;

/**
 * @author 阮雪峰
 */
@Slf4j
@Service("adminUserService")
public class AdminUserServiceImpl implements AdminUserService {


    private final AdminUserDao adminUserDao;
    private final UserPermissionCacheService userPermissionCacheService;
    private final RoleDao roleDao;
    private final SimpMessagingTemplate simpMessagingTemplate;

    private final AdminUserMapper adminUserMapper;

    public AdminUserServiceImpl(UserPermissionCacheService userPermissionCacheService, AdminUserDao adminUserDao, RoleDao roleDao, SimpMessagingTemplate simpMessagingTemplate, AdminUserMapper adminUserMapper) {
        this.userPermissionCacheService = userPermissionCacheService;
        this.adminUserDao = adminUserDao;
        this.roleDao = roleDao;
        this.simpMessagingTemplate = simpMessagingTemplate;
        this.adminUserMapper = adminUserMapper;
    }

    @ReadOnly
    @Override
    public PageDto<AdminUserDto> find(AdminUserListQueryDto query) {
        Page<AdminUserEntity> page = adminUserDao.findAll(query);
        return new PageDto<AdminUserDto>()
                .setPageSize(query.getPageSize())
                .setPage(query.getPage())
                .setTotal((int) page.getTotalElements())
                .setTotalPage(page.getTotalPages())
                .setRows(page.getContent().stream().map(adminUserMapper::toDto).toList());
    }

    @Commit
    @Override
    public void save(SaveAdminUserDto dto) {
        byte[] key = SecureUtil.generateKey(SymmetricAlgorithm.DES.getValue()).getEncoded();
        String salt = Base64.encode(key);
        DES des = SecureUtil.des(key);

        AdminUserEntity user = new AdminUserEntity();
        user.setUsername(dto.getUsername());
        user.setEmail(dto.getEmail());
        user.setGender(dto.getGender());
        user.setSalt(salt);
        user.setPassword(des.encryptBase64(Constant.INITIAL_PASSWORD));
        adminUserDao.save(user);
    }

    @Commit
    @Override
    public void delete(Long id) {
        adminUserDao.deleteById(id);
        userPermissionCacheService.remove(id);
    }

    @ReadOnly
    @Override
    public Boolean isEmailExist(String email) {
        return adminUserDao.findByEmail(email).isPresent();
    }

    @Commit
    @Override
    public void resetPassword(Long id) {
        byte[] key = SecureUtil.generateKey(SymmetricAlgorithm.DES.getValue()).getEncoded();
        String salt = Base64.encode(key);
        DES des = SecureUtil.des(key);
        String password = des.encryptBase64(Constant.INITIAL_PASSWORD);

        adminUserDao.findById(id).ifPresent(adminUser -> {
            adminUser.setPassword(password);
            adminUser.setSalt(salt);
            adminUserDao.save(adminUser);
        });
    }

    @Commit
    @Override
    public void updateRole(UpdateRoleDto dto) {
        List<RoleEntity> roles = Optional.ofNullable(dto.getRoleIdList())
                .filter(list -> !list.isEmpty())
                .map(list -> roleDao.findAllById(dto.getRoleIdList()))
                .orElse(Collections.emptyList());

        List<AdminUserEntity> users = adminUserDao.findAllById(dto.getIds());
        users.forEach(adminUser -> {
            adminUser.setRoles(roles);
            adminUser.setHomePageId(dto.getHomePageId());
            adminUserDao.save(adminUser);
        });
        adminUserDao.saveAll(users);
    }

    @Commit
    @Override
    public void update(UpdateAdminUserDto dto) {
        adminUserDao.findById(dto.getId())
                .ifPresent(adminUser -> {
                    BeanUtil.copyProperties(dto, adminUser, "id");
                    adminUserDao.save(adminUser);
                });
    }

    @Override
    public List<Long> findRoleIdList(Long id) {
        return adminUserDao.findById(id)
                .map(AdminUserEntity::getRoles)
                .map(roles -> {
                    List<Long> list = new ArrayList<>(roles.size());
                    roles.forEach(role -> list.add(role.getId()));
                    return list;
                }).orElse(new ArrayList<>());
    }

    @Override
    public Boolean isUsernameExist(String username) {
        return adminUserDao.findByUsername(username).isPresent();
    }

    @Override
    public Boolean isEmailExistWithId(String email, Long id) {
        return adminUserDao.findByIdNotAndEmail(id, email).isPresent();
    }

    @Override
    public Boolean isUsernameExistWithId(String username, Long id) {
        return adminUserDao.findByIdNotAndUsername(id, username).isPresent();
    }

    @Async
    @Override
    public void refreshPermissionAsync(Long id) {
        userPermissionCacheService.get(id)
                .ifPresent(dto -> {
                    log.info("刷新用户[{}]权限缓存", id);
                    userPermissionCacheService.refresh(id);
                    simpMessagingTemplate.convertAndSend(String.format(WebSocketUriConstant.NOTICE_UPDATE, id), true);
                    log.info("发送消息：{}", String.format(WebSocketUriConstant.NOTICE_UPDATE, id));
                });

    }

    @Async
    @Override
    public void refreshPermissionAsync(List<Long> ids) {
        ids.forEach(this::refreshPermissionAsync);
    }

}
